/**
 * Copyright(c) 2013 Jade Techonologies Co., Ltd.
 *
 * History:
 *   13-7-11 下午2:30 Created by sky
 */
package com.jade.modules.message.sms.bestone;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;

import com.jade.framework.base.comm.CommException;
import com.jade.framework.base.comm.ProtocolSession;
import com.jade.framework.base.lang.Hex;
import com.jade.framework.base.util.DataUtils;
import com.jade.modules.message.sms.comm.*;
import io.netty.buffer.ByteBuf;
import org.apache.commons.io.IOUtils;

/**
 * 协议编解码器。
 *
 * @author sky
 * @version 1.0 13-7-11 下午2:30
 */
public class BestoneCodec
        extends AbstractCodec
{
    private static final String SEP = "&";

    protected int getLength(ByteBuf byteBuf)
    {
        int i = 0;
        for (; i < byteBuf.readableBytes(); i++) {
            if (byteBuf.getByte(i) == SEP.charAt(0)) {
                break;
            }
        }
        return DataUtils.getInt(new String(byteBuf.array(), 0, i), -1);
    }

    public SmsProtocolMessage decode(InputStream is)
            throws IOException, CommException
    {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        int b = is.read();
        int index = 0;
        while (b != '&' && index < 10) {
            buffer.put((byte) b);
            index++;
            b = is.read();
        }

        int length = DataUtils.getInt(new String(buffer.array(), 0, index), 0);

        byte[] bytes = new byte[length - index - 1];
        IOUtils.readFully(is, bytes);
        String str = new String(bytes, "GBK");
        String[] array = str.split(SEP);
        return doDecode(array);
    }

    protected SmsProtocolMessage doDecode(String[] array)
            throws IOException
    {
        // read command id
        int commandId = DataUtils.getInt(array[0], 0);
        // read sequence
        int sequenceId = DataUtils.getInt(array[1], 0);
        // crate message and read body
        switch (commandId) {
            case BestoneConstants.CMD_LOGIN:
                Login login = new Login(sequenceId);
                login.setUsername(array[2]);
                login.setPassword(array[3]);
                return login;
            case BestoneConstants.CMD_LOGIN_RESP:
                LoginResp loginResp = new LoginResp(sequenceId);
                loginResp.setStatus(DataUtils.getInt(array[2], 0));
                return loginResp;
            case BestoneConstants.CMD_SUBMIT:
                Submit submit = new Submit(sequenceId);
                submit.setFrom(array[2]);
                submit.setTo(array[3]);
                submit.setContent(array[4]);
                submit.setServiceCode(array[5]);
                return submit;
            case BestoneConstants.CMD_SUBMIT_RESP:
                SubmitResp submitResp = new SubmitResp(sequenceId);
                submitResp.setStatus(DataUtils.getInt(array[2], 0));
                if (array.length > 3) {
                    submitResp.setMessageId(array[3]);
                }
                return submitResp;
            case BestoneConstants.CMD_TIMEOUT_NOTICE:
                TimeoutNotice notice = new TimeoutNotice(sequenceId);
                notice.setStatus(DataUtils.getInt(array[2], 0));
                return notice;
            default:
                throw new IllegalArgumentException("Invalid Command ID:" + commandId);
        }
    }

    @Override
    protected SmsProtocolMessage doDecode(DataInputStream is, int length)
            throws IOException
    {
        // do nothing
        return null;
    }

    public void encode(ProtocolSession session, ByteBuf byteBuf, Object message)
            throws IOException
    {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        doEncode(session, baos, message);
        int length = baos.size();
        //int length = lenContainSelf ? size + lenByteCounts : size;
        length = String.valueOf(length).length() + 1 + length;
        byteBuf.writeBytes(String.valueOf(length).getBytes());
        byteBuf.writeBytes(SEP.getBytes());
        byteBuf.writeBytes(baos.toByteArray());
        if (session.getLog().isTraceEnabled()) {
            session.getLog().trace("=================MSG ENCODE==================");
            int i = byteBuf.readerIndex();
            byte[] msg = new byte[byteBuf.readableBytes()];
            byteBuf.readBytes(msg);
            byteBuf.readerIndex(i);
            String str = Hex.format(msg, 10, ' ');
            session.getLog().trace(str);
            session.getLog().trace("\n============================================");
        }
    }

//    public void encode(SmsProtocolMessage message, OutputStream os)
//            throws IOException, CommException
//    {
//        ByteArrayOutputStream bos = new ByteArrayOutputStream(256);
//        DataOutputStream dos1 = new DataOutputStream(bos);
//        doEncode(message, dos1);
//        int length = bos.size();
//        length = String.valueOf(length).length() + 1 + length;
//        os.write(String.valueOf(length).getBytes());
//        os.write(SEP.getBytes());
//        bos.writeTo(os);
//        os.flush();
//        bos.close();
//    }

    @Override
    protected void doEncode(SmsProtocolMessage message, DataOutputStream os)
            throws IOException
    {
        //write command id
        writeIntString(os, message.getCommandId(), 4);
        os.writeString(SEP);
        //write sequence
        writeIntString(os, message.getIntSequenceId(), 4);
        os.writeString(SEP);
        //write body
        if (message.isRequest()) {
            writeRequest((SmsRequest) message, os);
        }
        else {
            writeResponse((SmsResponse) message, os);
        }
    }

    protected void writeRequest(SmsRequest request, DataOutputStream os)
            throws IOException
    {
        if (request instanceof Login) {
            Login login = (Login) request;
            os.writeString(login.getUsername());
            os.writeString(SEP);
            os.writeString(login.getPassword());
        }
        else if (request instanceof Submit) {
            Submit submit = (Submit) request;
            os.writeString(submit.getTo());
            os.writeString(SEP);
            os.writeString(submit.getFrom());
            os.writeString(SEP);
            os.write(submit.getContent().getBytes("GBK"));
            os.writeString(SEP);
            os.writeString(submit.getServiceCode());
        }
        else if (request instanceof TimeoutNotice) {
            TimeoutNotice notice = (TimeoutNotice) request;
            writeIntString(os, notice.getStatus(), 3);
        }
    }

    protected void writeResponse(SmsResponse response, DataOutputStream os)
            throws IOException
    {
        if (response instanceof LoginResp) {
            LoginResp loginResp = (LoginResp) response;
            os.writeString(String.valueOf(loginResp.getStatus()));
        }
        else if (response instanceof SubmitResp) {
            SubmitResp submitResp = (SubmitResp) response;
            writeIntString(os, submitResp.getStatus(), 3);
            os.writeString(SEP);
            os.writeString(submitResp.getMessageId());
        }
    }

    protected void writeIntString(DataOutputStream os, int value, int length)
            throws IOException
    {
        String str = String.format("%0" + length + "d", value);
        os.writeString(str);
    }
}
